package com.github.sbb.sherry.primitives.fetcher.redis;

import com.github.sbb.sherry.common.base.Preconditions;
import com.github.sbb.sherry.common.base.ValidateResult;
import com.github.sbb.sherry.primitives.config.datasource.DataSource;
import com.github.sbb.sherry.primitives.config.datasource.DataSourceQueryException;
import com.github.sbb.sherry.primitives.config.datasource.RedisDataSourceConfig;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.RedisURI.Builder;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.support.ConnectionPoolSupport;
import java.time.Duration;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;


public class RedisDataSource implements DataSource {

  private GenericObjectPool<StatefulRedisConnection<String, String>> pool;

  public RedisDataSource(RedisDataSourceConfig config) {
    Preconditions.checkNotNull(config, "RedisDataSourceConfig can`t be null");
    ValidateResult validate = config.validate();
    Preconditions.checkArgument(validate.isPass(), validate.getMessage());

    RedisURI redisURI = Builder
        .redis(config.getHost(), config.getPort())
        .withDatabase(config.getDatabase())
        .withPassword(config.getPassword())
        .withTimeout(Duration.ofMillis(config.getTimeout()))
        .build();

    RedisClient client = RedisClient.create(redisURI);

    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();

    poolConfig.setMinIdle(config.getMinIdle());
    poolConfig.setMaxIdle(config.getMaxIdle());
    poolConfig.setMaxTotal(config.getMaxActive());
    poolConfig.setMaxWaitMillis(config.getMaxWait());

    //noinspection unchecked
    pool = ConnectionPoolSupport
        .createGenericObjectPool(client::connect, poolConfig);
  }

  public String getByKey(String key) throws DataSourceQueryException {
    // executing work
    try (StatefulRedisConnection<String, String> connection = pool.borrowObject()) {
      RedisCommands<String, String> commands = connection.sync();
      return commands.get(key);
    } catch (Exception e) {
      throw new DataSourceQueryException(e);
    }
  }

  @Override
  public void check() {
    try {
      pool.preparePool();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public void close() {
    if (pool != null) {
      pool.close();
      pool = null;
    }
  }
}
